package logger

import (
	"context"
	"fmt"
	"os"
	"path"
	"strings"
	"time"

	"github.com/gookit/color"
)

// run 日志写入文件的主循环。
// 参数:
//
//	ctx: 上下文，用于控制协程的生命周期。
func (this *LoggerMgr) run(ctx context.Context) {
	//开始帧循环
	for {
		select {
		case <-ctx.Done():
			if this.writeChan == nil {
				return
			}
		//写日志到文件
		case logData, ok := <-this.writeChan:
			//判断通道是否关闭
			if !ok {
				return
			}

			//写日志准备
			this.writePrepare(logData.level)
			//写入普通日志数据
			_, err1 := this.file.WriteString(logData.data)
			//日志数据写入错误(尝试重新写一次)
			if err1 != nil {
				//关闭普通日志文件
				_ = this.file.Close()
				this.file = nil
				//写日志准备
				this.writePrepare(logData.level)
				//写入日志数据
				_, err2 := this.file.WriteString(logData.data)
				this.printError("<LoggerMgr:run()> file writeString err1:%s err2:%s", err1, err2)
			}
			//判断日志等级(错误日志会生成错误日志文件)
			if logData.level <= LogLevelError {
				//写入错误日志数据
				_, err := this.errorFile.WriteString(logData.data)
				if nil != err {
					//关闭错误日志文件
					_ = this.errorFile.Close()
					this.errorFile = nil
					this.printError("<LoggerMgr:run()> errorFile writeString error:%s", err)
				}
			}
		}
	}
}

// runByconsole 控制台日志输出的主循环。
// 参数:
//
//	ctx: 上下文，用于控制协程的生命周期。
func (this *LoggerMgr) runByconsole(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			if this.consoleChan == nil {
				return
			}
		case logData, ok := <-this.consoleChan:
			//判断通道是否关闭
			if !ok {
				return
			}

			//判断是否是有颜色的控制台输出
			this.printColour(logData.level, logData.data)
		}
	}
}

// writePrepare 准备日志文件写入。
// 参数:
//
//	logLevel: 日志等级。
func (this *LoggerMgr) writePrepare(logLevel logLevel) {
	//判断日志文件是周期内只会存在一个文件
	if this.config.LogFileMaxNumber < 1 {
		//创建周期内无编号后缀文件写入准备
		this.singleLogFilePrepare(logLevel)
	} else {
		//多日志文件 有后缀
		now := time.Now()
		//日期改变 新增文件
		newFile := this.needCreateNewFile(&now, this.fileUpdateTime)
		if newFile {
			this.fileUpdateTime = &now
			this.logFileNowLine = 0
			this.logFileNowNumber = LOGFILE_INIT_NUMBER
		}
		//更新
		this.logFileNowLine++
		//超过行数上限 新增文件
		newFileNum := false
		if this.logFileNowLine > this.config.LogFileMaxLine {
			newFileNum = true
			this.logFileNowLine = 0
			this.logFileNowNumber++
		}
		//日志文件不存在 或 需要新增文件
		if this.file == nil || newFile || newFileNum {
			_ = this.file.Close()
			//清理过期日志
			this.cleanExpireFiles()
			// 创建新文件
			for {
				filePathName := fmt.Sprintf("%s_%s_%d.log", path.Join(this.config.LogFilePath, this.config.LogFileName), this.getLogFileTimeSuffix(this.fileUpdateTime), this.logFileNowNumber)
				if _, err := os.Stat(filePathName); os.IsNotExist(err) {
					file, err := os.OpenFile(filePathName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
					if nil != err {
						this.printError("<LoggerMgr:writePrepare()> filePathName:%s openFile error:%s\n", filePathName, err)
					} else {
						this.file = file
						break
					}
				} else {
					this.logFileNowNumber++
				}
			}
		}
		//错误日志
		if logLevel <= LogLevelError {
			newErrorFile := this.needCreateNewFile(&now, this.errorFileUpdateTime)
			if this.errorFile == nil || newErrorFile {
				this.errorFileUpdateTime = &now
				if this.errorFile != nil {
					_ = this.errorFile.Close()
				}
				filePathName := fmt.Sprintf("%s_%s.error.log", path.Join(this.config.LogFilePath, this.config.LogFileName), this.getLogFileTimeSuffix(this.errorFileUpdateTime))
				file, err := os.OpenFile(filePathName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
				if err != nil {
					this.printError("logFile writePrepare() errorFile OpenFile %s error: %s\n", filePathName, err)
				}
				this.errorFile = file
			}
		}
	}
}

// singleLogFilePrepare 准备单个日志文件的写入。
// 参数:
//
//	logLevel: 日志等级。
func (this *LoggerMgr) singleLogFilePrepare(logLevel logLevel) {
	//检测普通日志文件
	if nil == this.file {
		//拼接普通日志文件全路径
		filePathName := fmt.Sprintf("%s.log", path.Join(this.config.LogFilePath, this.config.LogFileName))
		//打开普通日志文件
		file, err := os.OpenFile(filePathName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if nil != err {
			this.printError("<LoggerMgr:singleLogFilePrepare()> filePathName:%s openFile error:%s\n", filePathName, err)
		}
		this.file = file
	}
	//检测错误日志文件
	if logLevel <= LogLevelError {
		if nil == this.errorFile {
			//拼接错误日志文件全路径
			filePathName := fmt.Sprintf("%s.error.log", path.Join(this.config.LogFilePath, this.config.LogFileName))
			//打开错误日志文件
			file, err := os.OpenFile(filePathName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
			if nil != err {
				this.printError("<LoggerMgr:singleLogFilePrepare()> filePathName:%s openErrorFFile error:%s\n", filePathName, err)
			}
			this.errorFile = file
		}
	}
}

// needCreateNewFile 检查是否需要创建新的日志文件。
// 参数:
//
//	now: 当前时间。
//	lastTime: 上次创建文件的时间。
//
// 返回值:
//
//	bool: 是否需要创建新文件。
func (this *LoggerMgr) needCreateNewFile(now *time.Time, lastTime *time.Time) bool {
	y1, m1, d1 := lastTime.Date()
	yNow, mNow, dNow := now.Date()
	if y1 != yNow || m1 != mNow || d1 != dNow {
		return true
	}
	return false
}

// getLogFileTimeSuffix 获取日志文件的时间后缀。
// 参数:
//
//	t: 时间。
//
// 返回值:
//
//	string: 时间后缀。
func (this *LoggerMgr) getLogFileTimeSuffix(t *time.Time) string {
	return t.Format("20060102")

}

// cleanExpireFiles 清理过期的日志文件。
func (this *LoggerMgr) cleanExpireFiles() {
	//判断日志文件最大保存天数(<1:永久保存)
	if this.config.LogFileMaxSaveDays < 1 {
		return
	}
	//判断日志文件路径是否存在
	fileInfo, err := os.ReadDir(this.config.LogFilePath)
	if err != nil {
		_ = os.MkdirAll(this.config.LogFilePath, os.ModeDir)
		// this.printError("<LoggerMgr:cleanExpireFiles()> filePath:%s ReadDir err:%s", err, this.config.LogFilePath)
		return
	}
	//生成日志文件过期时间
	logFileExpireDate := time.Now().AddDate(0, 0, -this.config.LogFileMaxSaveDays)
	//判断文件夹内所有文件
	for _, info := range fileInfo {
		fileName := info.Name() //文件名字
		finfo, err := info.Info()
		if err != nil {
			this.printError("<LoggerMgr:cleanExpireFiles()> file not exist of :%v,%v,err=%v", fileName, info, err)
			continue
		}
		//删除过期文件
		if finfo.ModTime().Before(logFileExpireDate) && //判断文件是否过期
			strings.HasPrefix(fileName, this.config.LogFileName) && //判断文件前缀
			strings.HasSuffix(fileName, ".log") { //判断文件后缀
			//删除文件
			if err := os.Remove(path.Join(this.config.LogFilePath, fileName)); nil != err {
				this.printError("<LoggerMgr:cleanExpireFiles()> filePath:%s fileName:%s Remove err:%s", this.config.LogFilePath, fileName, err)
				//只要有一个文件删除失败则退出
				break
			}
			this.printLog("<cleanExpireFiles/日志管理:清理过期日志文件> 路径:%s 文件名:%s 移除成功", this.config.LogFilePath, fileName)
		}
	}
}

// printLog 输出普通日志到控制台。
// 参数:
//
//	format: 日志内容的格式化字符串。
//	a: 格式化参数。
func (this *LoggerMgr) printLog(format string, a ...interface{}) {
	//格式化输出信息
	msg := fmt.Sprintf("%s [logFile]: %s\n", time.Now().Format("2006-01-02 15:04:05"), fmt.Sprintf(format, a...))

	//普通打印
	fmt.Print(msg)
}

// printColour 控制台彩色日志输出。
// 参数:
//
//	level: 日志等级。
//	data: 日志内容。
func (this *LoggerMgr) printColour(level logLevel, data string) {
	switch level {
	case LogLevelFatal:
		color.Red.Print(data)
	case LogLevelError:
		color.Magenta.Print(data)
	case LogLevelWarn:
		color.Cyan.Print(data)
	case LogLevelInfo:
		color.Green.Print(data)
	case LogLevelDebug:
		color.Yellow.Print(data)
	default:
		color.Normal.Print(data)
	}
}
